package net.qiqbframework.loadhanding;

import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.common.utils.StringUtils;
import net.qiqbframework.config.ConfigurationException;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.annotation.ExecutableHandlerWrapper;
import net.qiqbframework.messaging.annotation.MessageExecutableHandler;
import net.qiqbframework.messaging.annotation.WrappedMessageExecutableHandler;
import net.qiqbframework.messaging.task.CurrentMessageTask;
import net.qiqbframework.messaging.task.MessageTask;

import javax.annotation.Nonnull;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Optional;

@Slf4j
public class LoadExecutableHandlerWrapper implements ExecutableHandlerWrapper {

    @Override
    public MessageExecutableHandler wrap(@Nonnull MessageExecutableHandler original) {
        final Optional<Object> routingBizIdentifierNaming = original.attribute(LoadHandlerAnnotationAttributes.ROUTING_BIZ_ID_NAMING);
        if (routingBizIdentifierNaming.isPresent()) {
            return new LoadMessageExecutableHandlerImpl(original);
        }
        return original;
    }

    private static class LoadMessageExecutableHandlerImpl extends WrappedMessageExecutableHandler implements LoadExecutableHandler {

        private String routingBizIdentifierNaming = null;

        private Class<?> aggregateType;

        private boolean supportBatch;

        private LoadMessageExecutableHandlerImpl(MessageExecutableHandler delegate) {
            super(delegate);
            // 获取返回值，
            final Optional<Method> originalMethodOptional = delegate.unwrap(Method.class);
            if (originalMethodOptional.isEmpty()) {

                final ConfigurationException configurationException = new ConfigurationException("The target object modified by @ LoadHandler must be a method");
                log.error("被 @LoadHandler 修饰目标对象必须是方法，但现在找不到。", configurationException);
                throw configurationException;
            }
            final Method method = originalMethodOptional.get();
            final Class<?> returnType = method.getReturnType();
            if (returnType.equals(Void.class)) {
                final ConfigurationException configurationException = new ConfigurationException("The method [%s] modified by @ LoadHandler must not be a void".formatted(method));
                log.error("被 @LoadHandler 修饰方法[{}]返回值不能是 void。", method, configurationException);
                throw configurationException;
            }
            supportBatch = (boolean) delegate.attribute(LoadHandlerAnnotationAttributes.SUPPORT_BATCH).orElseThrow();
            // 解析聚合根
            if (supportBatch) {
                initRoutingAggregateRootType(method);
            } else {
                this.aggregateType = method.getReturnType();
            }

            // init  bizIdentifierNaming
            this.routingBizIdentifierNaming = (String) delegate.attribute(LoadHandlerAnnotationAttributes.ROUTING_BIZ_ID_NAMING).orElseThrow();

            if (!StringUtils.nonEmptyOrNull(routingBizIdentifierNaming)) {
                // 不存在，默认找类型
            }
            // 验证

        }

        private void initRoutingAggregateRootType(Method method) {

            // 从返回值集合泛型中获取
            Type genericReturnType = method.getGenericReturnType();

            if (genericReturnType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
                // 获取实际类型参数
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

                for (Type actualTypeArgument : actualTypeArguments) {
                    this.aggregateType = (Class<?>) actualTypeArgument;
                }
            }
        }

        @Override
        public boolean canHandle(@Nonnull Message<?> message) {
            if (message instanceof LoadMessage<?, ?> loadMessage) {
                return loadMessage.getAggregateType().equals(aggregateType)
                        && loadMessage.getBusinessIdName().equals(routingBizIdentifierNaming);
            }
            return false;

        }

        @Override
        public Object handle(@Nonnull Message<?> message, Object target) throws Exception {
            LoadMessage<?, ?> loadMessage = (LoadMessage<?, ?>) message;
            final Object aggregate = loadMessage.getMetaData().get(AggregateDataProvider.AGGREGATE_ROOT_DATA_KEY);
            if (aggregate != null) {
                // 从缓存里拿出来，无需走加载
                // log.info("从缓存中获取聚合对象");
                return aggregate;
            }
            final Optional<MessageTask<?>> parent = CurrentMessageTask.get().parent();
            if (parent.isPresent()){
                final MessageTask<?> messageTask = parent.get();
            }

            final Object loadHandleResult = super.handle(loadMessage, target);

            if (supportBatch) {
                // 返回值是map，解析map，并将handler放到缓存中去
                if (loadHandleResult instanceof Map<?, ?> batchResult) {
                    setAggregateRootToLoadMessage(batchResult, loadMessage);
                }
                return loadMessage.getMetaData().get(AggregateDataProvider.AGGREGATE_ROOT_DATA_KEY);
            }
            // 查询
            return loadHandleResult;
        }

        private void setAggregateRootToLoadMessage(Map<?, ?> batchResult, LoadMessage<?, ?> loadMessage) {
            if (loadMessage == null) {
                return;
            }
            for (Map.Entry<?, ?> entry : batchResult.entrySet()) {
                if (loadMessage.getPayload().equals(entry.getKey())) {
                    loadMessage.andMetaData(Map.of(AggregateDataProvider.AGGREGATE_ROOT_DATA_KEY, entry.getValue()));
                    break;
                }
            }
            setAggregateRootToLoadMessage(batchResult, loadMessage.next());
        }

        @Override
        public Optional<String> bizIdentifierNaming() {
            return Optional.ofNullable(routingBizIdentifierNaming);
        }

        @Override
        public String aggregateNaming() {
            return aggregateType.getName();
        }

        @Override
        public Class<?> aggregateType() {
            return aggregateType;
        }

        @Override
        public Class<?> bizIdentifierType() {
            return null;
        }

        @Override
        public boolean supportPerBatch() {
            return supportBatch;
        }
    }
}
